package com.mrogrape.util;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;

/**
 * 提供一组针对对象操作的实用工具集。
 * 
 * <p>
 * 这个工具集主要提供了以下类型操作的工具：
 * <ul>
 * <li> 一个对象与多个对象的Equals比较。
 * </ul>
 * 
 * 
 * @author  Ice Spring
 * @version 
 */
public class ObjectUtils {

	/**
	 * 判断一个对象是否完全等于其他对象。
	 * 
	 * @param  obj    需要判断的对象,可以为null。
	 * @param  others 参照对象,至少存在一个，且不能为null。
	 * 
	 * @return 需要判断的对象是否完全等于其他对象的boolean值:
	 * 			true  : 完全相等。
	 * 			false : 不完全相等。
	 */
	public static boolean isAllEquals(Object obj,Object... others){

		for(int i = 0; i < others.length; i++)
		{
			if(!others[i].equals(obj)){
				return false;
			}
		}
		return true;
	}

	/**
	 * 快速判断一组对象是否全不为null。
	 * 
	 * @param objs
	 * @return
	 */
	public static boolean notNull(Object... objs){

		for(Object o : objs){
			if(o == null){
				return false;
			}
		}
		return true;
	}

	/**
	 * 提供一个可以用于任何对象的通用的{@link java.lang.Object#toString()}的实现
	 * 方案。
	 * 
	 * @param obj 任意对象，可以为null。
	 * @return 对该对象的分析结果。
	 */
	public static String toString(Object obj) {

		return new ObjectAnalyzer().toString(obj);
	}

	private static class ObjectAnalyzer{

		/**
		 * Converts an object to a string representation that lists all
		 * fields.
		 * 
		 * @param obj
		 *            an object
		 * @return a string with the object's class name and all field names
		 *         and values
		 */
		public String toString(Object obj) {

			if (obj == null) {
				return "null";
			}
			if (this.visited.contains(obj)) {
				return "...";
			}
			this.visited.add(obj);
			Class<?> cl = obj.getClass();
			if (cl == String.class) {
				return (String) obj;
			}
			if (cl.isArray()) {
				String r = cl.getComponentType() + "[]{";
				for (int i = 0; i < Array.getLength(obj); i++) {
					if (i > 0) {
						r += ",";
					}
					Object val = Array.get(obj, i);
					if (cl.getComponentType().isPrimitive()) {
						r += val;
					} else {
						r += this.toString(val);
					}
				}
				return r + "}";
			}

			StringBuilder sb = new StringBuilder();
			sb.append(cl.getName());
			// inspect the fields of this class and all superclasses
			do {
				sb.append("[");
				Field[] fields = cl.getDeclaredFields();
				AccessibleObject.setAccessible(fields, true);
				// get the names and values of all fields
				for (Field f : fields) {
					if (!Modifier.isStatic(f.getModifiers())) {
						if (!sb.toString().endsWith("[")) {
							sb.append(",");
						}
						sb.append(f.getName());
						sb.append("=");
						try {
							Class<?> t = f.getType();
							Object val = f.get(obj);
							if (t.isPrimitive()) {
								sb.append(val);
							} else {
								sb.append(this.toString(val));
							}
						} catch (Exception e) {
							e.printStackTrace();
						}
					}
				}
				sb.append("]");
				cl = cl.getSuperclass();
			} while (cl != null);
			return sb.toString();
		}


		private ArrayList<Object> visited = new ArrayList<Object>();

	}

	/**
	 * 通过方法名来调用对象中的的方法的方法。
	 * 
	 * <p>
	 * 这个方法可以通过方法名来动态调用某个对象中的方法。这在插件化编程中非常有用。此
	 * 方法会自动根据方法名和参数列表的类型来获取方法签名并通过反射机制调用。需要注意
	 * 注意的是，如果被调用的方法含有基本数据类型的参数，Java的自动包装机制将导致通过
	 * 包装器包装后的基本数据类型的Class类标记有别与基本数据类型的类标记，所以此时将
	 * 无法完成调用操作。如果需要调用这样的方法，那么请使用
	 * {@link ObjectUtils#invokeMetod(Object, String, Class[], Object[])}来手动
	 * 提供方法的签名信息。
	 * 
	 * <p>
	 * 注意：通过这种方式调用方法的运行速度将明显低于直接调用方法。
	 * 
	 * @param obj 需要被调用方法的对象。
	 * @param methodName 调用的方法名。
	 * @param args 调用的方法参数。
	 * @return 调用的方法的返回值。
	 * @throws SecurityException 如果没有权限或者访问的方法不可见。
	 * @throws NoSuchMethodException 如果不存在指定的方法。
	 * @throws IllegalArgumentException 如果参数不匹配。
	 * @throws IllegalAccessException 如果访问时发生异常。
	 * @throws InvocationTargetException 如果调用方法时发生例外。
	 * @see #invokeMetod(Object, String, Class[], Object[])
	 */
	public static Object invokeMethod(Object obj, String methodName, Object... args)
	throws SecurityException, NoSuchMethodException,
	IllegalArgumentException, IllegalAccessException,
	InvocationTargetException {

		// Arguments check
		if (obj == null) {
			throw new IllegalArgumentException(
			"Parameter 'bean' can not be null!");
		} else if (StringUtils.isEmpty(methodName)) {
			throw new IllegalArgumentException(
			"Parameter 'method' can not be empry!");
		}

		Class<?>[] pcs = new Class<?>[args.length];
		for (int i = 0; i < args.length; i++) {
			pcs[i] = args[i].getClass();
		}
		return ObjectUtils.invoke(obj, methodName, pcs, args);
	}

	/**
	 * 通过方法签名来调用一个对象中的方法的方法。
	 * 
	 * <p>
	 * 此方法可以通过方法的签名（方法名，参数配列）来调用一个对象中的指定方法。
	 * 
	 * @param obj 需要被调用方法的对象。
	 * @param methodName 被调用的方法名。
	 * @param paramTypes 调用的方法的参数类型。
	 * @param args 调用的方法的参数配列。
	 * @return 调用的方法的返回值。
	 * @throws SecurityException 如果没有权限或者访问的方法不可见。
	 * @throws NoSuchMethodException 如果不存在指定的方法。
	 * @throws IllegalArgumentException 如果参数不匹配。
	 * @throws IllegalAccessException 如果访问时发生异常。
	 * @throws InvocationTargetException 如果调用方法时发生例外。
	 * @see #invokeMethod(Object, String, Object...)
	 */
	public static Object invokeMetod(Object obj, String methodName,
			Class<?>[] paramTypes, Object[] args) throws SecurityException,
			IllegalArgumentException, NoSuchMethodException,
			IllegalAccessException, InvocationTargetException {

		// Arguments check
		if (obj == null) {
			throw new IllegalArgumentException(
			"Parameter 'bean' can not be null!");
		} else if (StringUtils.isEmpty(methodName)) {
			throw new IllegalArgumentException(
			"Parameter 'method' can not be empry!");
		} else if (paramTypes == null){
			throw new IllegalArgumentException(
			"Parameter 'paramTypes' can not be empry!");
		} else if (args == null){
			throw new IllegalArgumentException(
			"Parameter 'args' can not be empry!");
		} else if (paramTypes.length!=args.length){
			throw new IllegalArgumentException(
			"Parameters and their type is no match");
		}

		return ObjectUtils.invoke(obj, methodName, paramTypes, args);
	}

	/**
	 * 通过反射来调用对象的方法的方法。
	 */
	private static Object invoke(Object bean, String name,
			Class<?>[] paramTypes, Object[] args) throws SecurityException,
			NoSuchMethodException, IllegalArgumentException,
			IllegalAccessException, InvocationTargetException {

		Class<?> cl = bean.getClass();

		Method method = null;
		if(args == null){
			method = cl.getMethod(name);
		}else{
			method = cl.getMethod(name, paramTypes);
		}
		return method.invoke(bean, args);
	}
}
